package cn.org.tpeach.nosql.controller;


import cn.org.tpeach.nosql.constant.PublicConstant;
import cn.org.tpeach.nosql.enums.RedisTreeType;
import cn.org.tpeach.nosql.framework.LarkFrame;
import cn.org.tpeach.nosql.framework.ServiceProxy;
import cn.org.tpeach.nosql.redis.bean.DbAmount;
import cn.org.tpeach.nosql.redis.bean.RedisConnectInfo;
import cn.org.tpeach.nosql.redis.bean.RedisKeyInfo;
import cn.org.tpeach.nosql.redis.bean.RedisTreeItem;
import cn.org.tpeach.nosql.redis.connection.RedisLarkPool;
import cn.org.tpeach.nosql.redis.controller.BaseController;
import cn.org.tpeach.nosql.redis.controller.ResultRes;
import cn.org.tpeach.nosql.redis.service.IRedisConfigService;
import cn.org.tpeach.nosql.redis.service.IRedisConnectService;
import cn.org.tpeach.nosql.service.ICommonService;
import cn.org.tpeach.nosql.tools.ConfigParserUtils;
import cn.org.tpeach.nosql.tools.DataStructureUtils;
import cn.org.tpeach.nosql.tools.ReflectUtil;
import cn.org.tpeach.nosql.tools.StringUtils;
import cn.org.tpeach.nosql.view.JavaFxUtil;
import cn.org.tpeach.nosql.view.RLabel;
import cn.org.tpeach.nosql.view.RedisTreeCell;
import io.lettuce.core.KeyScanCursor;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Dialog;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.io.IOException;
import java.time.Clock;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;


public class TreeController  {
    private TreeView<RedisTreeItem> redisTree;
    private TextField keyFilterField;
    private MainController mainController;
    private final IRedisConfigService redisConfigService = ServiceProxy.getBeanProxy("redisConfigService", IRedisConfigService.class);
    private final IRedisConnectService redisConnectService = ServiceProxy.getBeanProxy("redisConnectService", IRedisConnectService.class);
    private final ICommonService commonService = ServiceProxy.getBeanProxy("commonService", ICommonService.class);

    private   ImageView connectIcon,disConnectIcon,pubsubIcon,editIcon,reloadIcon,deleteIcon,delete2Icon,addKeyIcon,flushDbIcon,renameIcon,copyIcon,openIcon;
    private ContextMenu contextMenu;
    private TreeController(MainController mainController) {
        this.redisTree = mainController.getRedisTree();
        this.keyFilterField = mainController.getKeysFilterBoxTextField();
        this.mainController = mainController;
        initTree();
    }

    public static  TreeController buildTree( MainController controller){
       return new TreeController( controller );
    }

    private void initTree(){
        //加载配置
        List<RedisConnectInfo> redisConfigList = redisConfigService.getRedisConfigAllList();
        TreeItem<RedisTreeItem> root = new TreeItem< >(RedisTreeItem.builder().name("root").originName("root").build());
        redisTree.setRoot(root);
//        redisTree.setEditable(true);
        if( DataStructureUtils.isNotEmpty(redisConfigList)){
            for (RedisConnectInfo redisConnectInfo : redisConfigList) {
                RedisTreeItem treeItem = RedisTreeItem.builder()
                        .id(redisConnectInfo.getId())
                        .name(redisConnectInfo.getName())
                        .originName(redisConnectInfo.getName())
                        .type(RedisTreeType.SERVER)
                        .build();
                root.getChildren().add(new TreeItem<>(treeItem));
            }
        }
        mainController.hasServerData();
        redisTree.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> {
            Node node = e.getPickResult().getIntersectedNode();
            TreeItem<RedisTreeItem> selectedItem = redisTree.getSelectionModel().getSelectedItem();
            if(node instanceof StackPane || node instanceof RLabel || node instanceof RedisTreeCell || selectedItem == null){
                return;
            }
            if(node instanceof HBox){
                HBox hBox = (HBox) node;
                ObservableList<Node> children = hBox.getChildren();
                if(DataStructureUtils.isNotEmpty(children)){
                    for (Node child : children) {
                        if(child.isDisable()){
                            return;
                        }
                    }
                }
            }else {
                if(node.isDisable()){
                    return;
                }
            }
            if (e.getButton() == MouseButton.SECONDARY){
                rightClickTreeNode(e);
            }else if(e.getButton() == MouseButton.PRIMARY){
                clickTreeNode(e);
            }
        });
        redisTree.setCellFactory(param -> new RedisTreeCell());
    }



    private void clickTreeNode(MouseEvent e){

        TreeItem<RedisTreeItem> selectedItem = redisTree.getSelectionModel().getSelectedItem();
        if(selectedItem == null){
            return;
        }
        //判断是否已经加载过
        if (DataStructureUtils.isNotEmpty(selectedItem.getChildren())) {
            return;
        }
        RedisTreeItem value = selectedItem.getValue();
        switch (value.getType()) {
            case SERVER:
                //单击加载db数据库数量
                openConnectRedisTree(selectedItem,value);
                break;
            case DATABASE:
                openDbRedisTree(selectedItem,value,false);
                break;
            case KEY:
                //打开标签
                openKeyInfoTab(selectedItem,value);
                break;
            default:
        }
    }

    private void openKeyInfoTab(TreeItem<RedisTreeItem> selectedItem, RedisTreeItem value) {
        String type ="keyInfo";
        String typeKey ="type";
        TabPane mainInfoTabPane = mainController.getMainInfoTabPane();
        Tab selectTab = mainInfoTabPane.getSelectionModel().getSelectedItem();
        Tab newKeyInfoTab = null;
        Object userData = null;
        //查找已打开的是否存在
        ObservableList<Tab> tabs = mainInfoTabPane.getTabs();
        for (int i = tabs.size() - 1; i > 0; i--) {
            Tab tab = tabs.get(i);
            userData = tab.getUserData();
            if(userData == value && type.equals(tab.getProperties().get(typeKey))){
                newKeyInfoTab = tab;
                break;
            }
        }
        if(newKeyInfoTab == null){
            if(selectTab != null){
                //判断当前选择的是否是keyInfo标签
                userData = selectTab.getUserData();
                if(userData instanceof RedisTreeItem && type.equals(selectTab.getProperties().get(typeKey))){
                    newKeyInfoTab = selectTab;
                }
            }
            if(newKeyInfoTab == null){
                for (int i = tabs.size() - 1; i > 0; i--) {
                    Tab tab = tabs.get(i);
                    userData = tab.getUserData();
                    if(userData instanceof RedisTreeItem && type.equals(tab.getProperties().get(typeKey))){
                        newKeyInfoTab = tab;
                        break;
                    }
                }
            }
        }

        if(newKeyInfoTab == null){
            newKeyInfoTab = openNewKeyInfoTab(selectedItem, value);
        }else{
            newKeyInfoTab.setText(JavaFxUtil.tabString(getKeyInfoTabText(value)));
            newKeyInfoTab.getTooltip().setText(getKeyInfoTabText(value));
            newKeyInfoTab.getProperties().put("selectedItem",selectedItem);
            newKeyInfoTab.setUserData(value);
            KeyInfoController keyInfoController = (KeyInfoController) newKeyInfoTab.getProperties().get("controller");
            keyInfoController.setSelectedItem(selectedItem);
        }
        KeyInfoController controller = (KeyInfoController) newKeyInfoTab.getProperties().get("controller");
        controller.refresh(true,true);
        mainInfoTabPane.getSelectionModel().select(newKeyInfoTab);


    }

    private Tab openNewKeyInfoTab(TreeItem<RedisTreeItem> selectedItem, RedisTreeItem value) {
        try {
            Tab newKeyInfoTab = new Tab(JavaFxUtil.tabString(getKeyInfoTabText(value)));
            TabPane mainInfoTabPane = mainController.getMainInfoTabPane();
            FXMLLoader fxmlLoader = new FXMLLoader();
            BorderPane borderPane = JavaFxUtil.loadFxml(fxmlLoader,"/view/fxml/keyInfo.fxml");
            borderPane.setMinHeight(mainInfoTabPane.getHeight());
            borderPane.setMinWidth(mainInfoTabPane.getWidth());
            mainInfoTabPane.widthProperty().addListener((observable, oldValue, newValue) -> borderPane.setMinWidth(newValue.doubleValue()));
            mainInfoTabPane.heightProperty().addListener((observable, oldValue, newValue) -> borderPane.setMinHeight(newValue.doubleValue()));
            KeyInfoController controller = fxmlLoader.getController();
            controller.setSelectedItem(selectedItem);
            controller.setTab(newKeyInfoTab);
            newKeyInfoTab.setContent(borderPane);
            newKeyInfoTab.getProperties().put("controller",controller);
            newKeyInfoTab.getProperties().put("selectedItem",selectedItem);
            newKeyInfoTab.getProperties().put("type","keyInfo");
            newKeyInfoTab.setUserData(value);
            mainInfoTabPane.getTabs().add(newKeyInfoTab);
            newKeyInfoTab.setOnClosed(event -> {
                newKeyInfoTab.getProperties().clear();
                newKeyInfoTab.setUserData(null);
            });
            newKeyInfoTab.setTooltip(new Tooltip(getKeyInfoTabText(value)));
            return newKeyInfoTab;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    private String getKeyInfoTabText(RedisTreeItem value){
        return value.getName()+"|"+value.getServerName()+"|DB"+value.getDbAmount().getDbIndex();
    }

    private void rightClickTreeNode(MouseEvent e){
        TreeItem<RedisTreeItem> selectedItem = redisTree.getSelectionModel().getSelectedItem();
        if(selectedItem == null){
            return;
        }
        RedisTreeItem value = selectedItem.getValue();
        switch (value.getType()) {
            case SERVER:
                serverMenuContext(e,selectedItem,value);
                break;
            case DATABASE:
                dataBaseMenuContext(e,selectedItem,value);
                break;
            case KEY:
                keyMenuContext(e,selectedItem,value);
                break;
            case KEY_NAMESPACE:
                nameSpaceMenuContext(e,selectedItem,value);
                break;
            default:
        }

    }



    private void serverMenuContext(MouseEvent e, TreeItem<RedisTreeItem> selectedItem, RedisTreeItem value) {
        boolean hasChild = DataStructureUtils.isNotEmpty(selectedItem.getChildren());
        connectIcon = JavaFxUtil.createIcon(connectIcon,"/view/img/menu/connect.png");
        disConnectIcon = JavaFxUtil.createIcon(disConnectIcon,"/view/img/menu/disconnect.png");
        pubsubIcon = JavaFxUtil.createIcon(pubsubIcon,"/view/img/menu/subscribe.png");
        editIcon = JavaFxUtil.createIcon(editIcon,"/view/img/menu/edit.png");
        reloadIcon = JavaFxUtil.createIcon(reloadIcon,"/view/img/menu/reload.png");
        contextMenu = ReflectUtil.nullToNewObj(contextMenu,ContextMenu.class);
        MenuItem connectItem = new MenuItem("连接",connectIcon);
        MenuItem disConnectItem = new MenuItem("断开连接",disConnectIcon);
        MenuItem pubsubItem = new MenuItem("订阅发布",pubsubIcon);
        MenuItem reloadItem = new MenuItem("重新加载",reloadIcon);
        connectItem.setDisable(hasChild);
        disConnectItem.setDisable(!hasChild);
//        pubsubItem.setDisable(!hasChild);
        pubsubItem.setDisable(true);
        reloadItem.setDisable(!hasChild);

        disConnectItem.setAccelerator(new KeyCodeCombination(KeyCode.D,KeyCombination.CONTROL_DOWN));

        connectItem.setOnAction(event -> openConnectRedisTree(selectedItem,value));
        disConnectItem.setOnAction(event -> {
            value.incrConnecting(-1);
            RedisLarkPool.destory(value.getId());
            selectedItem.getChildren().clear();
            TabPane mainInfoTabPane = mainController.getMainInfoTabPane();
            ObservableList<Tab> tabs = mainInfoTabPane.getTabs();
            if(DataStructureUtils.isNotEmpty(tabs)){
                Iterator<Tab> iterator = tabs.iterator();
                while (iterator.hasNext()){
                    Tab tab  = iterator.next();
                    if(tab.getUserData() != null && tab.getUserData() instanceof RedisTreeItem
                            && StringUtils.equals(((RedisTreeItem)tab.getUserData()).getId(),value.getId())){
                        JavaFxUtil.closeTabRelease(RedisApplication.mainController.getMainInfoTabPane(),tab);
                        iterator.remove();
                    }
                }
            }
        });
        reloadItem.setOnAction(event -> {
            RedisLarkPool.destory(value.getId());
            selectedItem.getChildren().clear();
            openConnectRedisTree(selectedItem,value);
        });

        contextMenu.getItems().clear();
        contextMenu.getItems().addAll(connectItem,disConnectItem,pubsubItem,reloadItem);
        contextMenu.show(LarkFrame.primaryStage, e.getScreenX(), e.getScreenY());

    }
    private void dataBaseMenuContext(MouseEvent e, TreeItem<RedisTreeItem> selectedItem, RedisTreeItem value) {
        contextMenu = ReflectUtil.nullToNewObj(contextMenu,ContextMenu.class);
        addKeyIcon = JavaFxUtil.createIcon(addKeyIcon,"/view/img/menu/add_key.png");
        deleteIcon = JavaFxUtil.createIcon(deleteIcon,"/view/img/delete.png");
        delete2Icon = JavaFxUtil.createIcon(delete2Icon,"/view/img/menu/delete.png");
        reloadIcon = JavaFxUtil.createIcon(reloadIcon,"/view/img/menu/reload.png");
        flushDbIcon = JavaFxUtil.createIcon(flushDbIcon,"/view/img/menu/data_reset.png");
        MenuItem addItem = new MenuItem("添加键",addKeyIcon);
        MenuItem  deleteItems = new MenuItem("删除指定键",delete2Icon);
        MenuItem batchDeleteItem = new MenuItem("批量删除",deleteIcon);
        MenuItem reloadItem = new MenuItem("重新加载",reloadIcon);
        MenuItem flushDbItem = new MenuItem("清空",flushDbIcon);

        reloadItem.setAccelerator(new KeyCodeCombination(KeyCode.R,KeyCombination.CONTROL_DOWN));

        addItem.setOnAction(event -> addKeyInfo(event, selectedItem, value));


        reloadItem.setOnAction(event -> {
            selectedItem.getChildren().clear();
            openDbRedisTree(selectedItem,value,false);
        });

        flushDbItem.setOnAction(event -> {
            boolean b = JavaFxUtil.showConfirmDialogYNC(LarkFrame.primaryStage, "Flush Database", null, "Do you really want to remove all keys from this database");
            if(b){
                ResultRes<String> res = BaseController.dispatcher(() -> redisConnectService.flushDb(value.getId(), value.getDbAmount().getDbIndex()));
                if(res.isRet()){
                    selectedItem.getChildren().clear();
                    value.getDbAmount().setDbSize(0L);
                    value.setName(value.getDbAmount().toString());
                    value.setOriginName(value.getDbAmount().toString());
                    Event.fireEvent(selectedItem, new TreeItem.TreeModificationEvent<>(TreeItem.valueChangedEvent(), selectedItem, selectedItem.getValue()));
                }else{
                    JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,res.getMsg());
                }
            }
        });
        batchDeleteItem.setOnAction(event -> {
            TextInputDialog dialog = new TextInputDialog("*");
            dialog.setContentText("请输入删除键的检索表达式:");
            dialog.setHeaderText(null);
            dialog.setTitle("批量删除");
            Optional<String> result = dialog.showAndWait();
            result.ifPresent(name ->{
                if(StringUtils.isNotBlank(name)){
                    showDeleteKeysDialog(name,value,(count,time)->{
                        boolean b = JavaFxUtil.showConfirmDialogYNC(LarkFrame.primaryStage, "删除成功", null,
                                "删除 " + name + " 数量: " + count + ",耗时：" + time + "毫秒, 是否重新加载db" + value.getDbAmount().getDbIndex()
                        );
                        if(b){
                            openDbRedisTree(selectedItem,value,true);
                        }
                    });
                }else{
                    JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,"Pattern cannot be null");
                }
            });

        });

        deleteItems.setOnAction(event -> {
            BorderPane borderPane = new BorderPane();
            TextArea textArea = new TextArea();
            textArea.setPromptText("使用\\n或,分隔");
            borderPane.setCenter(textArea);
            AtomicBoolean canClose = new AtomicBoolean(false);
            Dialog<String> dialog = JavaFxUtil.createDialog(borderPane);
            dialog.setTitle("删除指定键");
            ButtonType configButtonType = new ButtonType("确定", ButtonBar.ButtonData.OK_DONE);
            dialog.getDialogPane().getButtonTypes().addAll(configButtonType, ButtonType.CANCEL);
            dialog.setResult(StringUtils.EMPTY);
            Button configButton = (Button) dialog.getDialogPane().lookupButton(configButtonType);
            Button cancelButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.CANCEL);
            configButton.setDisable(true);
            textArea.textProperty().addListener((observable, oldValue, newValue) -> {
                configButton.setDisable(StringUtils.isBlank(newValue));
            });

            cancelButton.setOnAction(event1 -> {
                canClose.set(true);
                dialog.close();
            });

            configButton.setOnAction(event1 -> {
                boolean b = JavaFxUtil.showConfirmDialogYNC("删除确认", "是否确认删除？");
                if(b){
                    canClose.set(true);
                    String text = textArea.getText();
                    text = text.replaceAll("\\n", ",");
                    String[] split = text.split(",");
                    byte[][] bytes = Arrays.stream(split).map(item -> ConfigParserUtils.getInstance().strToByte(item)).toArray(size -> new byte[size][]);
                    ResultRes<Long> res = BaseController.dispatcher(() ->redisConnectService.deleteKeys(value.getId(), value.getDbAmount().getDbIndex(), bytes));
                    if(res.isRet()) {
                        JavaFxUtil.showMessageInfoDialog(null,"删除完成",null,"删除数量："+res.getData());
                        dialog.close();
                    }else{
                        canClose.set(false);
                        JavaFxUtil.showMessageErrorDialog(null,null,res.getMsg());
                    }
                }
            });
            dialog.setOnCloseRequest(event1 -> {
                if(!canClose.get()){
                    event1.consume();
                }
            });
            dialog.show();
        });

        contextMenu.getItems().clear();
        contextMenu.getItems().addAll(addItem,deleteItems,batchDeleteItem,reloadItem,flushDbItem);
        contextMenu.show(LarkFrame.primaryStage, e.getScreenX(), e.getScreenY());
    }

    private void showDeleteKeysDialog(String pattern, RedisTreeItem value, BiConsumer<Long,Long> success){
        try {
            long startTime = System.currentTimeMillis();
            ResultRes<KeyScanCursor<byte[]>> res = BaseController.dispatcher(() -> redisConnectService.getKeys(value.getId(), value.getDbAmount().getDbIndex(), pattern, true ));
            if(res.isRet()){
                AtomicBoolean canClose = new AtomicBoolean(false);
                List<byte[]> keys = res.getData().getKeys();
                if(DataStructureUtils.isEmpty(keys)){
                    JavaFxUtil.showMessageInfoDialog(LarkFrame.primaryStage,null,pattern + "匹配key数量为0");
                    return;
                }
                String timeConsuming = ((System.currentTimeMillis() - startTime))+"ms";
                String totalKeys = res.getData().getCursor();
                FXMLLoader fxmlLoader = new FXMLLoader();
//            Node node = FXMLLoader.load(getClass().getResource("/view/fxml/addServer.fxml"));
                Node node = JavaFxUtil.loadFxml(fxmlLoader, "/view/fxml/batchDeleteKey.fxml");
                Dialog<String> dialog = JavaFxUtil.createDialog(node);
                dialog.setTitle("Delete Keys");
                ButtonType configButtonType = new ButtonType("确定", ButtonBar.ButtonData.OK_DONE);
                dialog.getDialogPane().getButtonTypes().addAll(configButtonType, ButtonType.CANCEL);
                BatchDeleteKeysController controller = fxmlLoader.getController();
                dialog.getDialogPane().setContent(node);
                Button configButton = (Button) dialog.getDialogPane().lookupButton(configButtonType);
                Button cancelButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.CANCEL);
                RedisConnectInfo redisConfig = redisConfigService.getRedisConfigById(value.getId());
                controller.getBatchDeleteRedisServerText().setText(redisConfig.getHost());
                controller.getBatchDeleteDatabaseIndexText().setText(value.getDbAmount().getDbIndex()+"");
                controller.getBatchDeleteKeyPatternText().setText(pattern);
                controller.getBatchDeleteAffectedKeysText().setText(totalKeys);
                controller.getBatchDeleteShowKeysText().setText(keys.size()+"");
                controller.getBatchDeleteTimeConsumingText().setText(timeConsuming);
                TextArea batchDeleteTextArea = controller.getBatchDeleteTextArea();
                cancelButton.setOnAction(event -> {
                    canClose.set(true);
                    dialog.close();
                });
                configButton.setOnAction(event -> {
                    boolean b = JavaFxUtil.showConfirmDialogYNC( "删除确认",  "是否确认删除");
                    if(b){
                        canClose.set(true);
                        long startMillis = Clock.systemDefaultZone().millis();
                        ResultRes<Long> resultRes = BaseController.dispatcher(() ->redisConnectService.deleteKeys(value.getId(), value.getDbAmount().getDbIndex(),pattern,Integer.valueOf(totalKeys)));
                        if(resultRes.isRet()){
                            success.accept(resultRes.getData(),Clock.systemDefaultZone().millis()- startMillis);
                            dialog.close();
                        }else{
                            canClose.set(false);
                            JavaFxUtil.showMessageErrorDialog(null,null,resultRes.getMsg());
                        }
                    }else{
                        canClose.set(false);
                    }
                });
                dialog.setOnCloseRequest(event -> {
                    if(!canClose.get()){
                        event.consume();
                    }
                });
                dialog.show();
                Platform.runLater(()->{
                    StringBuilder sb = new StringBuilder();
                    keys.forEach(k->sb.append(batchDeleteTextArea.getText()+StringUtils.showHexStringValue(ConfigParserUtils.getInstance().byteToStr(k))+"\n"));
                    batchDeleteTextArea.setText(sb.toString());
                });
            }else{
                JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,res.getMsg());
            }


        }catch (IOException ex){
            ex.printStackTrace();
        }
    }


    private void addKeyInfo(Event event,TreeItem<RedisTreeItem> selectedItem, RedisTreeItem value){
        try {
            FXMLLoader fxmlLoader = new FXMLLoader();
            Node node  =  JavaFxUtil.loadFxml(fxmlLoader,"/view/fxml/addKey.fxml");
            Dialog<Object> dialog = JavaFxUtil.createDialog(node);
            ButtonType configButtonType = new ButtonType("确定", ButtonBar.ButtonData.OK_DONE);
            dialog.getDialogPane().getButtonTypes().addAll(configButtonType, ButtonType.CANCEL);
            AddKeyController controller = fxmlLoader.getController();
            Button configButton = (Button) dialog.getDialogPane().lookupButton(configButtonType);
            configButton.setDisable(true);
            ComboBox<RedisTreeType> addKeyComboType = controller.getAddKeyComboType();
            TextField addKeyNameField = controller.getAddKeyNameField();
            TextArea addKeyValueTextArea = controller.getAddKeyValueTextArea();
            TextField addKeyTtlField = controller.getAddKeyTtlField();
            TextField addKeyScoreOrField = controller.getAddKeyScoreOrField();
            ChangeListener<String> listener = (observable, oldValue, newValue) -> {
                configButton.setDisable(checkAddKeyInfo(addKeyNameField, addKeyTtlField, addKeyScoreOrField, addKeyValueTextArea, addKeyComboType));
            };
            addKeyNameField.textProperty().addListener(listener);
            addKeyValueTextArea.textProperty().addListener(listener);
            addKeyTtlField.textProperty().addListener(listener);
            addKeyScoreOrField.textProperty().addListener(listener);
            addKeyComboType.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
                configButton.setDisable(checkAddKeyInfo(addKeyNameField, addKeyTtlField, addKeyScoreOrField, addKeyValueTextArea, addKeyComboType));
            });
            configButton.setOnAction(event1 -> {

                Long ttl ;
                try {
                    ttl = Long.valueOf(addKeyTtlField.getText());
                    if (ttl != -1 && ttl < 0) {
                        JavaFxUtil.showMessageErrorDialog(null,null,"请输入正确时效(-1或者大于0)");
                        dialog.show();
                        return;
                    }
                } catch (NumberFormatException ex) {
                    JavaFxUtil.showMessageErrorDialog(null,null,"请输入正确时效(-1或者大于0)");
                    dialog.show();
                    return;
                }
                RedisKeyInfo keyInfo = new RedisKeyInfo();
                keyInfo.setId(value.getId());
                keyInfo.setDb(value.getDbAmount().getDbIndex());
                keyInfo.setKey(ConfigParserUtils.getInstance().strToByte(addKeyNameField.getText()));
                keyInfo.setTtl(ttl);
                keyInfo.setType(addKeyComboType.getValue());
                keyInfo.setValue(ConfigParserUtils.getInstance().strToByte(addKeyValueTextArea.getText()));
                switch (keyInfo.getType()){
                    case ZSET:
                        try {
                            keyInfo.setScore(Double.valueOf(addKeyScoreOrField.getText()));
                        } catch (NumberFormatException ez) {
                            JavaFxUtil.showMessageErrorDialog(null,null,"请输入正确的分数");
                            dialog.show();
                            return;
                        }
                        break;
                    case HASH:
                        keyInfo.setField(ConfigParserUtils.getInstance().strToByte(addKeyScoreOrField.getText()));
                        break;
                    default:
                        break;
                }
                //添加数据
                ResultRes<RedisKeyInfo> res = BaseController.dispatcher(() -> redisConnectService.addSingleKeyInfo(keyInfo));
                if(res.isRet()){
                    boolean b = JavaFxUtil.showConfirmDialogYNC(LarkFrame.primaryStage, "key was added", null, "key was added.Do you want to reload keys in selected database");
                    if(b){
                        openDbRedisTree(selectedItem,value,false);
                    }
                }else{
                    JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,res.getMsg());
                }
            });
            dialog.show();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private boolean checkAddKeyInfo(TextField addKeyNameField,TextField addKeyTtlField,TextField addKeyScoreOrField, TextArea addKeyValueTextArea,ComboBox addKeyComboType){
        boolean flag = addKeyNameField.getText().trim().isEmpty() || addKeyValueTextArea.getText().trim().isEmpty() || addKeyTtlField.getText().trim().isEmpty();
        if(flag || RedisTreeType.ZSET.equals(addKeyComboType.getValue()) || RedisTreeType.HASH.equals(addKeyComboType.getValue())){
            flag = addKeyScoreOrField.getText().trim().isEmpty();
        }
        return flag;
    }


    private void keyMenuContext(MouseEvent e, TreeItem<RedisTreeItem> selectedItem, RedisTreeItem value) {
        contextMenu = ReflectUtil.nullToNewObj(contextMenu,ContextMenu.class);
        openIcon = JavaFxUtil.createIcon(openIcon,"/view/img/menu/open.png");
        renameIcon = JavaFxUtil.createIcon(renameIcon,"/view/img/menu/rename.png");
        copyIcon = JavaFxUtil.createIcon(copyIcon,"/view/img/menu/copy.png");
        deleteIcon = JavaFxUtil.createIcon(deleteIcon,"/view/img/menu/delete.png");
        MenuItem openItem = new MenuItem("打开新的标签",openIcon);
        MenuItem ranameItem = new MenuItem("重命名",renameIcon);
        MenuItem copyItem = new MenuItem("复制",copyIcon);
        MenuItem deleteItem = new MenuItem("删除",deleteIcon);
        copyItem.setOnAction(event -> {
            Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
            Transferable tText = new StringSelection(ConfigParserUtils.getInstance().byteToStr(value.getKey()));
            clip.setContents(tText, null);
        });
        deleteItem.setOnAction(event -> commonService.deleteKey(selectedItem,value));

        ranameItem.setOnAction(event -> commonService.renameKey(selectedItem,value));
        openItem.setOnAction(event -> {
            TabPane mainInfoTabPane = mainController.getMainInfoTabPane();
            Tab tab = openNewKeyInfoTab(selectedItem, value);
            mainInfoTabPane.getTabs().add(tab);
            mainInfoTabPane.getSelectionModel().select(tab);
            KeyInfoController controller = (KeyInfoController) tab.getProperties().get("controller");
            controller.refresh(true,true);
        } );

        contextMenu.getItems().clear();
        contextMenu.getItems().addAll(openItem,ranameItem,copyItem,deleteItem);
        contextMenu.show(LarkFrame.primaryStage, e.getScreenX(), e.getScreenY());
    }


    private void nameSpaceMenuContext(MouseEvent e, TreeItem<RedisTreeItem> selectedItem, RedisTreeItem value) {
        contextMenu = ReflectUtil.nullToNewObj(contextMenu,ContextMenu.class);
        reloadIcon = JavaFxUtil.createIcon(reloadIcon,"/view/img/menu/reload.png");
        deleteIcon = JavaFxUtil.createIcon(deleteIcon,"/view/img/menu/delete.png");
        MenuItem reloadItem = new MenuItem("重新加载",reloadIcon);
        MenuItem deleteItem = new MenuItem("删除",deleteIcon);

        reloadItem.setOnAction(event -> {
            String keyPattern = value.getOriginName() + value.getNameSpaceSepartor()+"*";
            ResultRes<KeyScanCursor<byte[]>> dispatcher = BaseController.dispatcher(() -> redisConnectService.getKeys(value.getId(), value.getDbAmount().getDbIndex(), keyPattern, false));
            if (dispatcher.isRet()) {
                List<byte[]> keys = dispatcher.getData().getKeys();
                drawRedisKeyTree(selectedItem,value,keys);
            } else {
                JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,"获取" + keyPattern + "失败");
            }
        });
        deleteItem.setOnAction(event -> {
            String keyPattern = value.getOriginName() + value.getNameSpaceSepartor()+"*";
            showDeleteKeysDialog(keyPattern,value,(count,time)->{
                JavaFxUtil.showMessageInfoDialog(LarkFrame.primaryStage,"删除成功",null,"删除" + keyPattern + "数量：" + count );
                if(DataStructureUtils.isNotEmpty(selectedItem.getChildren())){
                    selectedItem.getValue().setDelete(true);
                    selectedItem.getChildren().clear();
                }
            });
        });
        contextMenu.getItems().clear();
        contextMenu.getItems().addAll(reloadItem,deleteItem);
        contextMenu.show(LarkFrame.primaryStage, e.getScreenX(), e.getScreenY());
    }

    private TreeItem<RedisTreeItem> buildLoadingTreeItem(RedisTreeItem value) {
        return new TreeItem<>(RedisTreeItem.builder()
                .id(value.getId())
                .name("loading")
                .originName("loading")
                .type(RedisTreeType.LOADING)
                .build());
    }

    private void openConnectRedisTree(TreeItem<RedisTreeItem> selectedItem,RedisTreeItem value) {
        TreeItem<RedisTreeItem> loadingItem = buildLoadingTreeItem(value);
        selectedItem.getChildren().add(loadingItem);
        //展开
        selectedItem.setExpanded(true);
        Event.fireEvent(selectedItem, new TreeItem.TreeModificationEvent<>(TreeItem.valueChangedEvent(), selectedItem, selectedItem.getValue()));
        selectedItem.getValue().incrConnecting(1);
        LarkFrame.executorService.execute(()->{

            ResultRes<List<DbAmount>> res = BaseController.dispatcher(() -> redisConnectService.getDbAmountAndSize(value.getId()));
            Platform.runLater(()->{
                boolean isRet = false;
                try {
                    if(selectedItem.getValue().isClose()){
                        if(res.isRet()){
                            RedisLarkPool.destory(value.getId());
                        }
                        return;
                    }
                    selectedItem.getValue().incrConnecting(-1);
//                Event.fireEvent(selectedItem, new TreeItem.TreeModificationEvent<>(TreeItem.valueChangedEvent(), selectedItem, selectedItem.getValue()));
                    if(res.isRet()){
                        //打开服务信息
                        commonService.openServerInfoTab(selectedItem);
                        List<DbAmount> dbAmountAndSize = res.getData() ;
                        for (DbAmount dbAmount : dbAmountAndSize) {
                            RedisTreeItem   redisTreeItem = RedisTreeItem.builder()
                                    .id(value.getId())
                                    .name(dbAmount.toString())
                                    .dbAmount(dbAmount)
                                    .parentItem(value)
                                    .originName(dbAmount.toString())
                                    .type(RedisTreeType.DATABASE)
                                    .build();
                            TreeItem<RedisTreeItem> treeItem = new TreeItem<>(redisTreeItem);
                            selectedItem.getChildren().add(treeItem);
                        }
                        //展开
                        selectedItem.getChildren().remove(loadingItem);
                        isRet = true;
                        selectedItem.setExpanded(true);
                        mainController.updateStatus(selectedItem);
                    }else{
                        JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage,null,res.getMsg());
                    }
                } finally{
                    if(!isRet){
                         selectedItem.getChildren().remove(loadingItem);
                    }
                }
            });
        });
    }



    public void openDbRedisTree(TreeItem<RedisTreeItem> selectedItem,RedisTreeItem value,boolean isReload) {
        if(DataStructureUtils.isNotEmpty(selectedItem.getChildren())){
            selectedItem.getChildren().clear();
        }
        TreeItem<RedisTreeItem> loadingItem = buildLoadingTreeItem(value);
        selectedItem.getChildren().add(loadingItem);
        //展开
        selectedItem.setExpanded(true);
        LarkFrame.executorService.execute(()->{
            try {
                ResultRes<Long> resultRes = BaseController.dispatcher(() -> redisConnectService.getDbKeySize(value.getId(), value.getDbAmount().getDbIndex()));
                boolean totalPattren = StringUtils.isNotBlank(keyFilterField.getText()) && StringUtils.isNotBlank(keyFilterField.getText().replaceAll("\\*",""));
                if (resultRes.isRet()) {
                    ResultRes<KeyScanCursor<byte[]>> res = BaseController.dispatcher(() -> redisConnectService.getKeys(value.getId(), value.getDbAmount().getDbIndex(), keyFilterField.getText(), totalPattren));
                    Platform.runLater(() -> {
                        try {
                            if (res.isRet()) {
                                KeyScanCursor<byte[]> data = res.getData();
                                final List<byte[]> keys = data.getKeys();
                                DbAmount dbAmount = value.getDbAmount();
                                dbAmount.setDbSize(resultRes.getData());
                                if (totalPattren) {
                                    dbAmount.setMatchingQuantity(Integer.valueOf(data.getCursor()));
                                } else {
                                    dbAmount.setMatchingQuantity(-1);
                                }
                                value.setName(dbAmount.toString());
                                value.setOriginName(dbAmount.toString());
                                Event.fireEvent(selectedItem, new TreeItem.TreeModificationEvent<>(TreeItem.valueChangedEvent(), selectedItem, selectedItem.getValue()));
                                drawRedisKeyTree(selectedItem, value, keys);
                                selectedItem.setExpanded(true);
                                Event.fireEvent(selectedItem, new TreeItem.TreeModificationEvent<>(TreeItem.valueChangedEvent(), selectedItem, selectedItem.getValue()));
                            } else {
                                JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage, null, res.getMsg());
                            }
                        }finally {
                            selectedItem.getChildren().remove(loadingItem);
                        }
                    });
                } else {
                    Platform.runLater(() -> {
                        selectedItem.getChildren().remove(loadingItem);
                        JavaFxUtil.showMessageErrorDialog(LarkFrame.primaryStage, null, resultRes.getMsg());
                    });
                }
            }finally {
                selectedItem.getChildren().remove(loadingItem);
            }
        });

    }


    private void drawRedisKeyTree(TreeItem<RedisTreeItem> treeNode, final RedisTreeItem redisTreeItem, final List<byte[]> keys){
        if(treeNode.getChildren() != null && !treeNode.getChildren().isEmpty()){
            treeNode.getChildren().clear();
        }
        if (DataStructureUtils.isNotEmpty(keys)) {
            boolean isReload = RedisTreeType.KEY_NAMESPACE.equals(redisTreeItem.getType());
            List<byte[]> sortKeys = keys;
            List<TreeItem<RedisTreeItem>> lastRedisNodeList = new ArrayList<>();
            RedisConnectInfo connectInfo = redisConfigService.getRedisConfigById(redisTreeItem.getId());
            String pattern = connectInfo.getNameSpaceSepartor();
            String lastKeys = null;
            if(StringUtils.isBlank(pattern)){
                pattern = PublicConstant.NAMESPACE_SPLIT;
            }
            RedisTreeItem treeItem = treeNode.getValue();
            for (byte[] keySrc : sortKeys) {
                String key = ConfigParserUtils.getInstance().byteToStr(keySrc);
                if(!Objects.isNull(key)){
                    if (key.contains(pattern)) {
                        //包含分隔符
                        String[] keySpace = key.split(pattern);
                        if (DataStructureUtils.isNotEmpty(keySpace)) {
                            TreeItem<RedisTreeItem> tempNode = treeNode;
                            int index = -1;
                            int parentIndex = index;
                            if (StringUtils.isNotBlank(lastKeys)) {
                                String[] lastKeysSpace = lastKeys.split(pattern);
                                for (int i = 0; i < keySpace.length; i++) {
                                    if (i >= lastKeysSpace.length || !keySpace[i].equals(lastKeysSpace[i])) {
                                        break;
                                    }
                                    index = i;
                                }
                                if (index != -1) {
                                    // 叶子节点相同
                                    int size = lastRedisNodeList.size();
                                    if (index == lastRedisNodeList.size() - 1) {
                                        parentIndex = index - 1;
                                    } else {
                                        parentIndex = index;
                                    }
                                    tempNode = lastRedisNodeList.get(parentIndex);
                                    // 移除后面的节点
                                    for (int i = size - 1; i > parentIndex; i--) {
                                        lastRedisNodeList.remove(i);
                                    }
                                } else {
                                    lastRedisNodeList.clear();
                                }
                            }
                            for (int i = parentIndex + 1; i < keySpace.length; i++) {
                                String nameSpaceKey = keySpace[i];
                                RedisTreeItem parentItem = tempNode.getValue();
                                if (i == keySpace.length - 1) {
                                    TreeItem<RedisTreeItem>  item = new TreeItem<>(RedisTreeItem.builder()
                                            .id(parentItem.getId())
                                            .key(keySrc)
                                            .name(StringUtils.showHexStringValue(key))
                                            .parentItem(parentItem)
                                            .dbAmount(parentItem.getDbAmount())
                                            .nameSpaceSepartor(pattern)
                                            .type(RedisTreeType.KEY)
                                            .build());
                                    item.getValue().setOriginName(item.getValue().getName());
                                    tempNode.getChildren().add(item);
                                    tempNode = item;

                                } else {
                                    if (!isReload) {
                                        TreeItem<RedisTreeItem>  item = new TreeItem<>(RedisTreeItem.builder()
                                                .id(parentItem.getId())
                                                .key(keySrc)
                                                .name(StringUtils.showHexStringValue(nameSpaceKey))
                                                .parentItem(parentItem)
                                                .dbAmount(parentItem.getDbAmount())
                                                .nameSpaceSepartor(pattern)
                                                .type(RedisTreeType.KEY_NAMESPACE)
                                                .build());
                                        if(RedisTreeType.KEY_NAMESPACE.equals(parentItem.getType())){
                                            item.getValue().setOriginName(parentItem.getOriginName() + pattern+item.getValue().getName());
                                        }else{
                                            item.getValue().setOriginName(item.getValue().getName());
                                        }

                                        tempNode.getChildren().add(item);
                                        tempNode = item;
                                    } else if(treeItem.getName().equals(nameSpaceKey)){
                                        isReload = false;
                                    }
                                }
                                lastRedisNodeList.add(tempNode);
                            }
                            lastKeys = key;
                        }
                    }else{
                        //直接添加节点
                        TreeItem<RedisTreeItem>  item = new TreeItem<>(RedisTreeItem.builder()
                                .id(redisTreeItem.getId())
                                .key(keySrc)
                                .name(StringUtils.showHexStringValue(key))
                                .parentItem(redisTreeItem)
                                .dbAmount(redisTreeItem.getDbAmount())
                                .nameSpaceSepartor(pattern)
                                .type(RedisTreeType.KEY)
                                .build());
                        item.getValue().setOriginName(item.getValue().getName());
                        treeNode.getChildren().add(item);
                    }
                }
            }
        }

    }
}
